/*******************************************************************************
* CSCI321 Major Project 2010 - University of Wollongong                        *
********************************************************************************
* Program:		WeaveEdit                                              *
* Authors:		Simon Allen (Student#: 3457527, Unix: sa655)           *
*			Scott Bellis (Student#: 3479110, Unix: sjb900)         *
*			Jason Benger (Student#: 3454137, Unix: jb960)          *
*			Timothy Newmarch (Student#: 3459263 Unix: tgn336)      *
*			Benjamin Richardson (Student# 3467752, Unix: bjr401)   *
* Supervisor:		Daniel F. Saffioti                                     *
* Modified:		28/10/2010                                             *
* Description:		WeaveEdit is a highly portable collaborative text      *
*			editor and word processor, allowing several users to   *
*			read and edit multiple documents at the same time.     *
*			It is available for Mac OS X, Windows and Linux,       *
*			utilising Nokia's Qt development framework.            *
*******************************************************************************/


#include "find.h"

#include <QApplication>
#include <QGridLayout>
#include <QColor>


/**
  * Create find widget.
  */
Find::Find(Editor *newEditor) : QWidget(newEditor)
{
	findOptions = new FindOptions(this);
	findText = new QLineEdit(this);
	opLabel = new QLabel(this);
	prevButton = new QPushButton(this);
	nextButton = new QPushButton(this);
	replaceLabel = new QLabel("with:", this);
	replaceText = new QLineEdit(this);
	replaceButton = new QPushButton(this);
	replaceAllButton = new QPushButton(this);
	editor = newEditor;

	QGridLayout *findGrid = new QGridLayout(this);
	QPushButton *optionsButton = new QPushButton(this);
	QPushButton *closeButton = new QPushButton(this);
	QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Maximum, QSizePolicy::Minimum);

	prevButton->setText("Previous");
	nextButton->setText("Next");
	replaceButton->setText("Replace");
	replaceAllButton->setText("Replace all");
	optionsButton->setText("Options");
	closeButton->setText("Close");

	prevButton->setIcon(QIcon::fromTheme("go-previous"));
	nextButton->setIcon(QIcon::fromTheme("go-next"));
	closeButton->setIcon(QIcon::fromTheme("window-close"));

	findGrid->addWidget(opLabel, 0, 0);
	findGrid->addWidget(findText, 0, 1);
	findGrid->addWidget(prevButton, 0, 2);
	findGrid->addWidget(nextButton, 0, 3);
	findGrid->addItem(spacerItem, 0, 4);
	findGrid->addWidget(optionsButton, 0, 5);
	findGrid->addWidget(closeButton, 0, 6);
	findGrid->addWidget(replaceLabel, 1, 0);
	findGrid->addWidget(replaceText, 1, 1);
	findGrid->addWidget(replaceButton, 1, 2);
	findGrid->addWidget(replaceAllButton, 1, 3);

	setVisible(false);
	setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);

	connect(findText, SIGNAL(textChanged(QString)), this, SLOT(find(QString)));
	connect(nextButton, SIGNAL(clicked()), this, SLOT(findNext()));
	connect(prevButton, SIGNAL(clicked()), this, SLOT(findPrev()));
	connect(replaceButton, SIGNAL(clicked()), this, SLOT(replace()));
	connect(replaceAllButton, SIGNAL(clicked()), this, SLOT(replaceAll()));
	connect(optionsButton, SIGNAL(clicked()), findOptions, SLOT(show()));
	connect(closeButton, SIGNAL(clicked()), this, SLOT(hide()));
}

/**
  * Enable the replace text part of the widget.
  */
void Find::enableReplace()
{
	opLabel->setText("Replace:");

	replaceLabel->show();
	replaceText->show();
	replaceButton->show();
	replaceAllButton->show();
}

/**
  * Disable the replace text part of the widget.
  */
void Find::disableReplace()
{
	opLabel->setText("Find:");

	replaceLabel->hide();
	replaceText->hide();
	replaceButton->hide();
	replaceAllButton->hide();
}

/**
  * Show the widget on screen, setting focus to the find input box so users don't have to click.
  */
void Find::show()
{
	QWidget::show();

	findText->setFocus();
}

/**
  * Finds text in the editor document from pos with or'd find flags.
  * if pos is < 0 the search is performed backwards.
  */
void Find::find(QString text, int pos, bool backwards)
{
	QColor backgroundColour;
	QPalette palette(findText->palette());

	if (text.length() > 0)
	{
		QTextDocument::FindFlags flags = findOptions->getOptions();

		if (backwards)
			flags |= QTextDocument::FindBackward;

		findCursor = editor->document()->find(text, pos, flags);

		if (findCursor.isNull())
		{
			findCursor.clearSelection();

			backgroundColour = "#FF9292";
		}

		else
			backgroundColour = qApp->palette().color(QPalette::Base);
	}

	else
	{
		findCursor.clearSelection();

		backgroundColour = qApp->palette().color(QPalette::Base);
	}

	palette.setColor(QPalette::Base, backgroundColour);
	findText->setPalette(palette);
	findText->setPalette(palette);

	editor->setTextCursor(findCursor);
}

/**
  * Find the next entry.
  */
void Find::findNext()
{
	if (findText->text().length() > 0)
	{
		findCursor = editor->document()->find(findText->text(), findCursor, findOptions->getOptions());

		// No more text found, loop back around to the start.
		if (findCursor.isNull())
			find(findText->text());

		else
			editor->setTextCursor(findCursor);
	}
}

/**
  * Find the previous entry.
  */
void Find::findPrev()
{
	if (findText->text().length() > 0)
	{
		findCursor = editor->document()->find(findText->text(), findCursor, QTextDocument::FindBackward);

		// No more found, loop back around to the end of the document.
		if (findCursor.isNull())
			find(findText->text(), editor->document()->characterCount() - 1, true);

		else
			editor->setTextCursor(findCursor);
	}
}

/**
  * Replace the currently selected word with the one in the replace entry box.
  */
void Find::replace()
{
	if (!findCursor.isNull() && (findText->text().length() > 0))
	{
		findCursor.removeSelectedText();
		findCursor.insertText(replaceText->text());

		editor->setTextCursor(findCursor);
	}
}

/**
  * Replace every occurance of a word with another.
  */
void Find::replaceAll()
{
	int pos = 0;
	QString text;

	if (!findCursor.isNull() && (findText->text().length() > 0))
	{
		// Must use a loop and manually replace each result or the sync protocol breaks.
		while (true)
		{
			text = editor->toPlainText();

			if (findOptions->getOptions() & QTextDocument::FindCaseSensitively)
				pos = text.indexOf(findText->text(), 0, Qt::CaseSensitive);

			else
				pos = text.indexOf(findText->text(), 0, Qt::CaseInsensitive);

			if (pos < 0)
				break;

			findCursor.setPosition(pos);
			findCursor.setPosition(pos + findText->text().length(), QTextCursor::KeepAnchor);
			findCursor.removeSelectedText();
			findCursor.insertText(replaceText->text());

			editor->setTextCursor(findCursor);
		}
	}
}
